/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.mybatis.autoconfigure

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils
import java.util.concurrent.TimeUnit
import org.springframework.boot.autoconfigure.AutoConfiguration
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.context.properties.bind.DefaultValue
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.PropertySource
import tony.core.PROJECT_GROUP
import tony.core.misc.YamlPropertySourceFactory
import tony.mybatis.interceptor.PaginationInnerInterceptor
import tony.mybatis.parser.JsqlParserGlobal
import tony.mybatis.parser.cache.JdkSerialCaffeineJsqlParseCache
import tony.mybatis.sqlinjector.TonySqlInjector

/**
 * mybatis plus 自动配置
 * @author tangli
 * @date 2025/09/22 17:06
 */
@PropertySource("classpath:mybatis-plus.config.yml", factory = YamlPropertySourceFactory::class)
@EnableAutoConfiguration(
    excludeName = [
        "org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration",
        "org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration",
        "org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration"
    ]
)
@EnableConfigurationProperties(MybatisPlusPaginationProperties::class)
@AutoConfiguration
private class MybatisPlusAutoConfiguration {
    @ConditionalOnMissingBean(PaginationInnerInterceptor::class)
    @Bean
    private fun paginationInnerInterceptor(
        dataSourceProperties: DataSourceProperties,
        mybatisPlusPaginationProperties: MybatisPlusPaginationProperties,
    ): PaginationInnerInterceptor =
        PaginationInnerInterceptor(JdbcUtils.getDbType(dataSourceProperties.url)).apply {
            maxLimit = mybatisPlusPaginationProperties.maxLimit
            isOverflow = mybatisPlusPaginationProperties.overflow
            isOptimizeJoin = mybatisPlusPaginationProperties.optimizeJoin
        }

    @Bean
    private fun mybatisPlusInterceptor(innerInterceptors: List<InnerInterceptor>): MybatisPlusInterceptor {
        val interceptor = MybatisPlusInterceptor()
        innerInterceptors.forEach { interceptor.addInnerInterceptor(it) }
        JsqlParserGlobal.setJsqlParseCache(
            JdkSerialCaffeineJsqlParseCache { cache ->
                cache
                    .maximumSize(1024)
                    .expireAfterAccess(5, TimeUnit.MINUTES)
                    .expireAfterWrite(5, TimeUnit.MINUTES)
            }
        )
        return interceptor
    }

    @Bean
    private fun tonySqlInjector(): TonySqlInjector =
        TonySqlInjector()
}

@ConfigurationProperties(
    prefix = "$PROJECT_GROUP.mybatis-plus.pagination",
    ignoreInvalidFields = false,
    ignoreUnknownFields = false
)
private class MybatisPlusPaginationProperties(
    val maxLimit: Long?,
    @DefaultValue("false")
    val overflow: Boolean,
    @DefaultValue("true")
    val optimizeJoin: Boolean,
)
